package com.hd.trans.network.component

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import android.util.SparseArray
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.hd.trans.R
import com.hd.trans.TranslationInterior
import com.hd.trans.common.Constants
import com.hd.trans.network.ApiServiceManager
import com.hd.trans.network.MultNetDisposableObserver
import com.hd.trans.network.RxSchedulers
import com.hd.trans.network.bean.MultDataResponse2
import com.hd.trans.network.bean.trans.TextTransNewReq
import com.hd.trans.network.bean.trans.TextTransNewRes
import com.hd.trans.offline.OfflineDisposableObserver
import com.hd.trans.share.Md5Util
import com.hd.trans.ui.base.AppContextProvider
import com.hd.trans.utils.CommonUtil
import com.hd.trans.utils.OfflineConstant
import com.hd.trans.utils.StringUtils
import com.translation.assistant.data.bean.TransResultBean
import com.translation.assistant.manager.OfflineTranslatorManager
import com.translation.assistant.utils.SensitiveWordsCheck
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.ObservableOnSubscribe
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers
import java.util.TreeMap

/**
 * 执行文本翻译：分成两条通道，一是在线翻译，二是离线翻译，两通道同时执行。
 * 情况一：在线翻译失败，离线翻译成功，翻译结果取离线翻译结果；
 * 情况二，离线翻译成功后5秒内在线翻译依旧没有反馈翻译结果，翻译结果取离线翻译结果；
 * 除以上两种情况外的都取在线翻译结果。离线翻译实质作为在线翻译的补助功能。
 */
class HdTranslateComponent2: LifecycleObserver {

    private var offlineManager: OfflineTranslatorManager
    private var isDestroy: Boolean = false
    private var mContext: Context
    private val mHandler = Handler(Looper.getMainLooper())
    /**
     * 离线翻译成功后单位时间（毫秒）内在线翻译依旧没有反馈翻译结果，翻译结果取离线翻译结果；
     */
    var offlineWaitTime = 5000L

    constructor() {
        this.mContext = AppContextProvider.getContext()
        offlineManager = OfflineTranslatorManager.getInstance(mContext)
    }

    constructor(context: Context) {
        this.mContext = context
        offlineManager = OfflineTranslatorManager.getInstance((mContext))
    }

    /**
     * 页面销毁 终止回调
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() {
        isDestroy = true
        offlineManager.destroy()
    }

    fun translationText(
        context: Context,
        origContent: String?,
        fromTranslateCode: String,
        toTranslateCode: String,
        isOpenOfflineTrans: Boolean = false,
        callback: TranslateCallback?,
    ) {
        this.mContext = context
        translationText(origContent, fromTranslateCode, toTranslateCode, isOpenOfflineTrans, callback)
    }

    fun translationText(
        origContent: String?,
        fromTranslateCode: String,
        toTranslateCode: String,
        isOpenOfflineTrans: Boolean = false,
        callback: TranslateCallback?,
    ) {
        //输入文本翻译前处理
        var origContentAfter = origContent
        if (origContentAfter.isNullOrEmpty()) {
            callback?.onTranslateFailed(
                Constants.ResponseCode.ERROR_COMM,
                mContext.getString(R.string.tips_empty_content)
            )
            return
        }
        if (fromTranslateCode == "en") {
            //英文翻译时，将下划线替换为中划线，否则内容翻译不了
            origContentAfter = origContentAfter.replace("_", "-")
        }

        val mSummaryRunnable = SummaryRunnable(fromTranslateCode, toTranslateCode, isOpenOfflineTrans, callback)

        if(isOpenOfflineTrans){
            offlineManager.checkLanguageCode(fromTranslateCode){ hadFromCode ->//是否下载了源语言离线包
                if(hadFromCode){
                    offlineManager.checkLanguageCode(toTranslateCode){ hadToCode ->//是否下载了目标语言离线包
                        if(hadToCode){
                            //离线翻译
                            translateOffline(origContentAfter, fromTranslateCode, toTranslateCode, object : TranslateCallback{
                                override fun onTranslateCompleted(
                                    data: String?,
                                    fromLangCode: String?,
                                    toLangCode: String?,
                                    status: Boolean,
                                ) {
                                    mSummaryRunnable.successOffline(data)
                                }

                                override fun onTranslateFailed(code: Int, msg: String?) {
                                    mSummaryRunnable.failedOffline(code, msg)
                                }

                            })
                        }else{
                            //自动下载离线包
                            downloadModel(toTranslateCode, mSummaryRunnable)
                        }
                    }
                }else{
                    //自动下载离线包
                    downloadModel(fromTranslateCode, mSummaryRunnable)
                }
            }
        }

        //在线翻译
        translateOnline(origContentAfter, fromTranslateCode, toTranslateCode, false, isOpenOfflineTrans, object : TextTranslateCallback{
            override fun startTranslate() {
            }

            override fun onTranslateCompleted(
                data: String?,
                bilingual: String?,
                fromLangCode: String?,
                toLangCode: String?,
                status: Boolean,
            ) {
                mSummaryRunnable.successOnline(data)
            }

            override fun onTranslateCompleted(
                data: String?,
                fromLangCode: String?,
                toLangCode: String?,
                status: Boolean,
            ) {
            }

            override fun onTranslateFailed(code: Int, msg: String?) {
                mSummaryRunnable.failedOnline(code, msg)
            }

        })
    }

    private fun downloadModel(languageCode: String, mSummaryRunnable: SummaryRunnable){
        if (OfflineConstant.checkNetwork(AppContextProvider.getContext()) == OfflineConstant.NETWORL_NONE) {
            //无网络
            mSummaryRunnable.failedOffline(
                Constants.ResponseCode.ERROR_NETWORK,
                mContext.getString(R.string.no_network_prompt_tips)
            )
            return
        }

        mSummaryRunnable.failedOffline(
            Constants.ResponseCode.ERROR_COMM,
            mContext.getString(R.string.trans_server_is_busy)
        )
        //静默下载
        offlineManager.downloadModel(languageCode)
    }

    /**文本快捷翻译（新接口）*/
    fun translateOnline(
        origContentAfter: String,
        fromLang: String,
        toLang: String,
        withDiff: Boolean = false,
        callback: TextTranslateCallback?,
    ) {
        translateOnline(origContentAfter, fromLang, toLang, withDiff, false, callback)
    }

    private fun translateOnline(
        origContentAfter: String,
        fromLang: String,
        toLang: String,
        withDiff: Boolean = false,
        isOpenOfflineTrans: Boolean = false,
        callback: TextTranslateCallback?,
    ) {
        if (OfflineConstant.checkNetwork(AppContextProvider.getContext()) == OfflineConstant.NETWORL_NONE) {
            //无网络
            if(!isOpenOfflineTrans){
                callback?.onTranslateFailed(
                    Constants.ResponseCode.ERROR_NETWORK,
                    mContext.getString(R.string.no_network_prompt)
                )
            }else{
                callback?.onTranslateFailed(
                    Constants.ResponseCode.ERROR_NETWORK,
                    mContext.getString(R.string.no_network_prompt_tips)
                )
            }

            return
        }

        val contentList = StringUtils.splitStringByLength(origContentAfter, Constants.MAX_TRANSLATE_SIZE)
        if (contentList.isNullOrEmpty()) {
            callback?.onTranslateFailed(
                Constants.ResponseCode.ERROR_COMM,
                mContext.getString(R.string.tips_empty_content)
            )
            return
        }

        val size = contentList.size
        val observables: Array<Observable<*>?> = arrayOfNulls(size)
        for (i in contentList.indices) {
            val req = TextTransNewReq()
            req.fromLang = fromLang
            req.toLang = toLang
            req.fromText = contentList[i]
            req.timestamp = CommonUtil.getServiceTimeStamp()
            req.withDiff = withDiff
            req.sign = sortByKey(req)
            observables[i] = ApiServiceManager.transitionNewApi
                .tansTextQuick(req)
                .map(Function { data: TextTransNewRes? ->
                    MultDataResponse2(
                        i.toString(),
                        data
                    )
                } as Function<TextTransNewRes?, MultDataResponse2>)
        }

        var fromLang: String? = null
        var toLang: String? = null
        Observable.mergeArray(*observables)
            .subscribeOn(Schedulers.io())
            .compose(RxSchedulers.applySchedulers())
            .subscribe(MultNetDisposableObserver(object :
                MultNetDisposableObserver.NetCallBack {
                private val stringMap = SparseArray<TextTransNewRes>()
                override fun onResponse(response: Any) {
                    if (response is MultDataResponse2) {
                        val index = StringUtils.toInt(response.requestTag, -1)
                        val resp = response.dataResponse as TextTransNewRes
                        if (index != -1) {
                            stringMap.put(index, resp)
                            fromLang = resp.fromLang
                            toLang = resp.toLang
                        }
                    }
                }

                override fun onError(code: Int, msg: String) {
                    if (!isDestroy) {
                        callback?.onTranslateFailed(code, msg)
                    }
                }

                override fun onTokenInvalid(msg: String) {
                    if (!isDestroy) {
                        callback?.onTranslateFailed(Constants.ResponseCode.ERROR_TOKEN, msg)
                    }
                }

                override fun onComplete() {
                    if (!isDestroy) {
                        val contentTranslate = StringBuilder()
                        val alignString = StringBuilder()
                        if (stringMap.size() > 0) {
                            var isDangerous = false
                            var errorMsg = ""
                            var errorCode = 0
                            for (i in 0 until stringMap.size()) {
                                //译文内容拼接
                                val mTextTransNewRes = stringMap[i]
                                if(!mTextTransNewRes.isStatus){
                                    errorMsg = mTextTransNewRes.reason?: mContext.getString(R.string.trans_server_is_busy)
                                    if(errorMsg == "Same lang"){
                                        errorCode = Constants.ResponseCode.ERROR_SAME_LANG
                                        errorMsg = mContext.getString(R.string.trans_same_lang)
                                    }else{
                                        errorCode = Constants.ResponseCode.ERROR_COMM
                                    }
                                }
                                if(i > 0){
                                    contentTranslate.append(" ")
                                }
                                contentTranslate.append(mTextTransNewRes.toText?:"")
                                isDangerous = isDangerous || mTextTransNewRes.isDanger

                                //双语对照内容拼接，按顺序
                                mTextTransNewRes.align?.let {
                                    for (index in 0 until it.size) {
                                        val value = it[index.toString()]
                                        value?.values?.forEach { align ->
                                            if(
                                                align.src.trim().isNotEmpty()
                                                && align.tgt.isNotEmpty()
                                            ){
                                                alignString.append(align.src)
                                                alignString.append("\n")
                                                alignString.append(align.tgt)
                                                alignString.append("\n\n")
                                            }
                                        }
                                    }
                                }

                            }
                            stringMap.clear()
                            val content = contentTranslate.toString()
                            val bilingual = alignString.toString()
                            if(content.trim().isEmpty()){
                                if(isDangerous){
                                    callback?.onTranslateFailed(
                                        Constants.ResponseCode.ERROR_IS_DANGER,
                                        mContext.getString(R.string.trans_content_error)
                                    )
                                }else{
                                    if(!TextUtils.isEmpty(errorMsg) && errorCode != 0){
                                        callback?.onTranslateFailed(
                                            errorCode,
                                            errorMsg
                                        )
                                    }else{
                                        callback?.onTranslateFailed(
                                            Constants.ResponseCode.ERROR_COMM,
                                            mContext.getString(R.string.trans_server_is_busy)
                                        )
                                    }
                                }
                            }else{
                                callback?.onTranslateCompleted(
                                    content,
                                    bilingual,
                                    fromLang,
                                    toLang,
                                    true)
                            }
                        }
                    }
                }
            }))
    }

    private fun sortByKey(req: TextTransNewReq): String{
        /**排序-拼接-md5*/
        //排序
        val map: MutableMap<String, String> = TreeMap { obj1, obj2 -> // 按照key的升序排序
            obj1?.compareTo(obj2!!)!!
        }
        map["fromLang"] = req.fromLang
        map["toLang"] = req.toLang
        map["fromText"] = req.fromText
        map["withDiff"] = req.withDiff.toString()
        map["timestamp"] = req.timestamp.toString()
        map["fromProduct"] = req.fromProduct

        val keySet: Set<String> = map.keys
        //拼接
        val it = keySet.iterator()
        var splicing = ""
        while (it.hasNext()) {
            val key = it.next()
            splicing += key + map[key]
        }
        splicing += "PONYTRANS2022"
        //md5
        Log.e("splicing","splicing = $splicing")
        val md5 = Md5Util.md5(splicing)
        Log.e("splicing","md5 = $md5")
        return md5
    }

    fun translateOffline(
        origContentAfter: String,
        fromTranslateCode: String,
        toTranslateCode: String,
        callback: TranslateCallback?,
    ){

        val contentList = StringUtils.splitStringByLength(origContentAfter, Constants.MAX_TRANSLATE_SIZE)
        if (contentList.isNullOrEmpty()) {
            callback?.onTranslateFailed(
                Constants.ResponseCode.ERROR_COMM,
                mContext.getString(R.string.tips_empty_content)
            )
            return
        }

        contentList.forEach { content ->
            if(SensitiveWordsCheck.isViolated(content)){
                //违规内容拦截
                callback?.onTranslateFailed(
                    Constants.ResponseCode.ERROR_IS_DANGER,
                    mContext.getString(R.string.trans_content_error)
                )
                return
            }
        }

        val size: Int = contentList.size
        val observables: Array<Observable<*>?> = arrayOfNulls(size)
        for (i in size - 1 downTo 0) {
            observables[i] =
                Observable.create<TransResultBean?>(ObservableOnSubscribe<TransResultBean?> {
                        emit: ObservableEmitter<TransResultBean?> ->
                    offlineManager.translate(
                        contentList[i],
                        fromTranslateCode,
                        toTranslateCode,
                        i
                    ){
                        emit.onNext(it)
                        emit.onComplete()
                    }

                })
                    .map(Function { bean -> bean })
        }
        Observable.mergeArray(*observables)
            .subscribeOn(Schedulers.io())
            .compose(RxSchedulers.applySchedulers())
            .subscribe(OfflineDisposableObserver(object : OfflineDisposableObserver.CallBack {
                private val stringMap = SparseArray<String>()
                override fun onNext(data: Any) {
                    if (data is TransResultBean) {
                        stringMap.put(data.index, data.context)
                    }
                }
                override fun onError(e: Throwable) {
                    if (!isDestroy) {
                        callback?.onTranslateFailed(
                            Constants.ResponseCode.ERROR_COMM,
                            "翻译失败"
                        )
                    }
                }
                override fun onComplete() {
                    if (!isDestroy) {
                        val contentTranslate = java.lang.StringBuilder()
                        if (stringMap.size() > 0) {
                            for (i in 0 until size) {
                                contentTranslate.append(stringMap[i]?:"")
                                contentTranslate.append(" ")
                            }

                            var result = contentTranslate.toString()

                            //违规内容拦截
                            if(SensitiveWordsCheck.isViolated(result)) {
                                callback?.onTranslateFailed(
                                    Constants.ResponseCode.ERROR_IS_DANGER,
                                    mContext.getString(R.string.trans_content_error)
                                )
                                return
                            }

                            //空字符拦截
                            if (result.trim { it <= ' ' }.isEmpty()) {
                                callback?.onTranslateFailed(
                                    Constants.ResponseCode.ERROR_COMM,
                                    mContext.getString(R.string.trans_server_is_busy)
                                )
                                return
                            }

                            callback?.onTranslateCompleted(
                                result,
                                fromTranslateCode,
                                toTranslateCode,
                                true)
                        } else {
                            callback?.onTranslateFailed(
                                Constants.ResponseCode.ERROR_COMM,
                                "翻译失败"
                            )
                        }
                    }
                }
            }))
    }

    inner class SummaryRunnable: Runnable{
        private var isFailedOnline = false
        private var isFailedOffline = false
        private var isSuccessOnline = false
        private var isSuccessOffline = false

        private var contentResult: String? = ""
        private var failedCode: Int = 0
        private var failedMsg: String? = ""

        private var fromTranslateCode: String = ""
        private var toTranslateCode: String = ""
        private var callback: TranslateCallback? = null
        private var isOpenOfflineTrans: Boolean = false

        constructor(
            fromTranslateCode: String,
            toTranslateCode: String,
            isOpenOfflineTrans: Boolean,
            callback: TranslateCallback?,
        ){
            this.fromTranslateCode = fromTranslateCode
            this.toTranslateCode = toTranslateCode
            this.callback = callback
            this.isOpenOfflineTrans = isOpenOfflineTrans
        }

        fun successOnline(result: String?){
            logDebug("在线 = $result")
            isSuccessOnline = true
            contentResult = result
            post()
        }

        fun successOffline(result: String?){
            logDebug("离线 = $result")
            isSuccessOffline= true
            if(isSuccessOnline) return
            contentResult = result
            if(isFailedOnline){
                post()
            }else{
                post(offlineWaitTime)
            }
        }

        fun failedOnline(code: Int, msg: String?){
            isFailedOnline = true
            if(failedCode != Constants.ResponseCode.ERROR_IS_DANGER){
                failedCode = code
                failedMsg = msg
            }
            if(!isOpenOfflineTrans || isFailedOffline || isSuccessOffline){
                post()
            }
        }

        fun failedOffline(code: Int, msg: String?){
            isFailedOffline = true
            if(code == Constants.ResponseCode.ERROR_IS_DANGER
                || code == Constants.ResponseCode.ERROR_NETWORK){
                failedCode = code
                failedMsg = msg
            }
            if(isFailedOnline){
                post()
            }
        }

        private fun post(time: Long = 0){
            //如果callback为空，整个runnable将毫无意义
            if(callback == null) return

            mHandler.removeCallbacks(this)
            if(time > 0){
                mHandler.postDelayed(this, time)
            }else{
                mHandler.post(this)
            }
        }

        override fun run() {
            if(isSuccessOnline || isSuccessOffline){
                logDebug("反馈 = $contentResult")
                callback?.onTranslateCompleted(
                    contentResult,
                    fromTranslateCode,
                    toTranslateCode,
                    isSuccessOnline //标记：返回的结果是在线翻译结果还是离线翻译结果
                )
                callback = null
            }
            else if(isFailedOnline || isFailedOffline){
                callback?.onTranslateFailed(failedCode, failedMsg)
                callback = null
            }
        }

        private fun logDebug(msg: String){
            if(TranslationInterior.isDebug())
                Log.d("HdTranslateComponent2", msg)
        }

    }
}